package polyinstanceof;

/**
 * @author shkstart
 * @date 2022/5/10 - 22:32
 */
/*
 * 面向对象特征之三：多态性
 *
 * 1.理解多态性：可以理解为一个事物的多种形态。
 * 2.何为多态性：
 *   对象的多态性：父类的引用指向子类的对象（或子类的对象赋给父类的引用）
 *
 * 3. 多态的使用：虚拟方法调用
 *   有了对象的多态性以后，我们在编译期，只能调用父类中声明的方法，但在运行期，我们实际执行的是子类重写父类的方法。
 *   总结：编译，看左边；运行，看右边。
 *
 * 4.多态性的使用前提：  ① 类的继承关系  ② 方法的重写
 *
 * 5.对象的多态性，只适用于方法，不适用于属性（编译和运行都看左边）
 */
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.eat();

        Man man = new Man();
        man.eat();
        man.age = 24;
        man.earnMoney();


        //*************************************************
        System.out.println("*******************");

        //对象的多态性：父类的引用指向子类的对象 （或子类的对象赋给父类的引用）

        Person p2 = new Man();

        //多态的使用：当调用子父类同名同参数的方法时，实际执行的是子类重写父类的方法 ---虚拟方法调用
        p2.eat();
        p2.walk();
//		p2.earnMoney(); //编译出错,因为在父类中就没定义过这个方法！
        System.out.println(p2.id); //1001

        System.out.println("**********************************");
//      不能调用子类特有的方法和属性,编译时，p2是Person类型
        p2.name = "Jay";
//        p2.earnMoney();
//        p2.isSmoking = true;
//        有了对象的多态性后，内存中实际上加载了子类特有的属性和方法，但是由于变量类型为父类类型
//        导致编译时，只能调用父类声明的属性和方法。子类特有的属性和方法不能调用。



        //如何才能调用子类特有的属性和方法？
        //向下转型：使用强制类型转换符。
        Man m1 = (Man)p2;
        m1.earnMoney();
        m1.isSmoking = true;
        //使用强转时，可能出现ClassCastException的异常。
//        Woman w1 = (Man)p2;
//        w1.goShopping();


        /*
         * instanceof关键字的使用
         *
         * a instanceof A:判断对象a是否是类A的实例。如果是，返回true；如果不是，返回false。
         *
         * 返回false的理解:说明super()找不到父类中有A的类型，说明它们有可能同等级
         *
         *
         *  使用情境：为了避免在向下转型时出现ClassCastException的异常，我们在向下转型之前，先
         *  进行instanceof的判断，一旦返回true，就进行向下转型。如果返回false，不进行向下转型。
         *
         *  ！！:如果 a instanceof A返回true,则 a instanceof B也返回true.
         *  其中，类B是类A的父类。         要求a和A要有子父类的关系
         *
         *  对 ！！的理解:如果 p2 instanceof Man 是true 则 p2 instanceof Person 也是true
         *  因为p2中的本质内容是Man的对象,而根据类的多态性,Man的构造器可以Super(Person)父类Person的
         *  结构,且Man是Person的子类实例化,而Person可以对自己实例化，对子类自然也可以
         *  if（左子 instanceof 右父）         就可以通过
         *
         *      Object anObject = new String();  //当new的是String()时输出yyy 当new的是Object()时不输出语句
                if(anObject instanceof String) { //左边的对象的内容必须比右边的类多
                        System.out.println("yyy");
                    }
         */

        if(p2 instanceof Woman) {
            System.out.println("*******Woman********");
        }
        if(p2 instanceof Man) {
            System.out.println("*******Man********");
        }



        if(p2 instanceof Person) {
            System.out.println("*******Person********");
        }
        if(p2 instanceof Object) {
            System.out.println("*******Object********");
        }

//        Woman w = new Man(); //不相关的俩个类是不可以赋值的
//        Person p3 = new Man();
//        Woman w4 = (Woman)p3;

        //练习:
        //问题一：编译时通过，运行时不通过
        //举例一：
//		Person p3 = new Woman();    //多态性
//		Man m3 = (Man)p3;           //骗过编译，运行不过，因为Woman和Man毫不相干
        //举例二：
//		Person p4 = new Person();   //因为p4的对象里面没有Man的属性方法，无法强制转换为Man
//		Man m4 = (Man)p4;           //比如想通过m4.isSmoking(); 但是呢，
                                    //p4对象里面没有定义过这个方法，所以报错


        //问题二：编译通过，运行时也通过
//		Object obj = new Woman();   //这个可以，因为多态性，然后由于Woman本身继承了Person的属性与方法
//		Person p = (Person)obj;     //所以当强制转换时可以调用Person的属性方法，也就可以运用

        //问题三：编译不通过
//		Man m5 = new Woman();

//		String str = new Date();

//		Object o = new Date();
//		String str1 = (String)o;



    }
}
